package org.geektimes.injection.context;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.geektimes.common.function.Attempt;
import org.geektimes.common.util.PropertiesUtil;
import org.geektimes.injection.component.Component;
import org.geektimes.injection.component.ControllerComponent;
import org.geektimes.injection.context.Initializer.ComponentInitializer;
import org.geektimes.injection.context.Initializer.ControllerComponentInitializer;
import org.geektimes.injection.context.Initializer.DefaultComponentInitializer;
import org.geektimes.injection.context.Initializer.IncludeComponentInitializer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletContext;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @FileName: ComponentContext
 * @Description: 容器管理上下文对象
 * @Author: MaWenHao
 * @Created: 2021/3/9 - 下午9:00
 */
public class ComponentContext {

    /**
     * 上下文对象名称
     */
    public static final String CONTEXT_NAME = ComponentContext.class.getSimpleName();

    /**
     * 内部组件类的全路径配置,以逗号分隔
     */
    public static final String COMPONENT_CLASS_NAMES = "component.class.names";

    /**
     * 外部组件类的全路径配置,以逗号分隔
     */
    public static final String OUT_COMPONENT_CLASS_NAMES = "out.component.class.names";

    /**
     * 程序主配置文件名称
     */
    private static final String APPLICATION_FILE_NAME = "application.properties";

    /**
     * 组件初期化器集合
     */
    private static List<ComponentInitializer> componentInitializers = new ArrayList<>();

    /**
     * 容器对象
     */
    private static final Map<String, Component> COMPONENT_CONTAINER = new HashMap<>();

    /**
     * Controller容器对象
     */
    private static final Map<String, ControllerComponent> CONTROLLER_COMPONENT_CONTAINER = new HashMap<>();

    /**
     * 应用配置
     */
    private static Properties properties;

    /**
     * web上下文对象
     */
    private static ServletContext servletContext;

    static {
        // 此处 DefaultComponentInitializer 默认初始化器一定要在容器的最后,作为兜底
        componentInitializers.add(new ControllerComponentInitializer());
        componentInitializers.add(new DefaultComponentInitializer());
    }


    public ComponentContext(ServletContext servletContext) {
        ComponentContext.servletContext = servletContext;
        servletContext.setAttribute(CONTEXT_NAME, this);
    }

    /**
     * 获取 ComponentContext
     *
     * @return
     */
    public static ComponentContext getInstance() {
        return (ComponentContext) servletContext.getAttribute(CONTEXT_NAME);
    }
    
    public ServletContext getServletContext() {
        return servletContext;
    }

    public Component getComponent(String id) {
        return COMPONENT_CONTAINER.get(id);
    }

    public void putComponent(String id, Component component) {
        COMPONENT_CONTAINER.put(id, component);
    }

    public <T> T getBean(String id) {
        return (T) getComponent(id).getBean();
    }

    public Map<String, Component> getContainer() {
        return COMPONENT_CONTAINER;
    }

    public void putControllerComponent(String path, ControllerComponent controllerComponent) {
        CONTROLLER_COMPONENT_CONTAINER.put(path, controllerComponent);
    }

    public Map<String, ControllerComponent> getControllerContainer() {
        return CONTROLLER_COMPONENT_CONTAINER;
    }

    public Properties getProperties() {
        return properties;
    }

    /**
     * 容器初始化
     */
    public void init() throws Exception {
        properties = PropertiesUtil.loadPropertyFile(APPLICATION_FILE_NAME);
        // TODO 组件扫描可优化为注解方式
        // 加载第三方组件
        initOutComponent();
        // 加载内部组件
        String componentClassNames = properties.getProperty(COMPONENT_CLASS_NAMES);
        if (StrUtil.isNotBlank(componentClassNames)) {
            // 加载对象到容器中
            loadComponent(componentClassNames);
            // 容器初始化
            initComponent();
        }
    }

    /**
     * 将内置组件加载到容器中
     * @param componentClassNames 组件全类名,以逗号分隔
     */
    private void loadComponent(String componentClassNames) {
        Arrays.stream(componentClassNames.split(","))
                .map(String::trim)
                .filter(StrUtil::isNotBlank)
                .map(Attempt.apply(ComponentContext.class.getClassLoader()::loadClass))
                .forEach(Attempt.accept(this::load));
    }

    /**
     * 内部组件初始化
     */
    private void initComponent() throws Exception {
        for (Map.Entry<String, Component> entry : COMPONENT_CONTAINER.entrySet()) {
            Component component = entry.getValue();
            Object bean = component.getBean();
            Class<?> beanClass = bean.getClass();

            // 注入属性
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Resource resource = declaredField.getAnnotation(Resource.class);
                if (ObjectUtil.isNotEmpty(resource)) {
                    String name = resource.name();
                    Object fieldValue = getBean(name);

                    if (declaredField.getType().isAssignableFrom(fieldValue.getClass())) {
                        declaredField.setAccessible(true);
                        declaredField.set(bean, fieldValue);
                        declaredField.setAccessible(false);
                    }

                }
            }

            // 内置组件初始化
            for (Method method : beanClass.getMethods()) {
                PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
                if (ObjectUtil.isNotEmpty(postConstruct)) {
                    method.invoke(bean);
                }
            }
        }
    }

    /**
     * 加载第三方组件
     */
    private void initOutComponent() {
        String outComponentClassNames = properties.getProperty(OUT_COMPONENT_CLASS_NAMES);
        if (StrUtil.isNotBlank(outComponentClassNames)) {
            Arrays.stream(outComponentClassNames.split(","))
                    .map(String::trim)
                    .filter(StrUtil::isNotBlank)
                    .map(Attempt.apply(ComponentContext.class.getClassLoader()::loadClass))
                    .filter(IncludeComponentInitializer.class::isAssignableFrom)
                    .map(Attempt.apply(Class::newInstance))
                    .map(IncludeComponentInitializer.class::cast)
                    .forEach(c -> c.init(this));

        }
    }

    /**
     * 加载所有的处理器方法
     *
     * @param aClass controller
     */
    private void load(Class<?> aClass) throws Exception {
        // 初始化组件
        ComponentInitializer componentInitializer = this.findComponentInitializer(aClass);
        componentInitializer.init(aClass, this);
    }

    private ComponentInitializer findComponentInitializer(Class<?> aClass) {
        for (ComponentInitializer componentInitializer : componentInitializers) {
            if (componentInitializer.match(aClass)) {
                return componentInitializer;
            }
        }
        return null;
    }

}
